﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "SamplePickNodeReferenceAction.h"
#include "IUiView.h"
#include "GbmpNew.h"
#include "ISelection.h"
#include "IPickEvent.h"
#include "GbmpWindows.h"
#include "IUiDocument.h"
#include "IHighlights.h"
#include "IGraphicsPlane.h"
#include "IActionManager.h"
#include "IPickCandidates.h"
#include "GcmpCommandNames.h"
#include "IUiDocumentViewManager.h"
#include "ICommandMenuItemDefinition.h"
#include "IMenuItemContainerDefinition.h"

#include "PickFilter.h"
#include "ActionUiBehavior.h"
//#include "PickSketchObjectsFirst.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace Sample;

namespace
{
    //查找某个元素所在位置的迭代器
    std::vector<gcmp::OwnerPtr<IGraphicsNodeReference>>::const_iterator FindInContainer(const std::vector<gcmp::OwnerPtr<IGraphicsNodeReference>>& container, const IGraphicsNodeReference* item)
    {
        for (auto iter = container.begin(); iter != container.end(); ++iter)
        {
            if ((*iter)->EqualTo(*item))
            {
                return iter;
            }
        }

        return container.end();
    }
}

SamplePickNodeReferenceAction::SamplePickNodeReferenceAction(PickNodeReferenceExchangeData& exchangeData,
    IPickNodeReferenceAction::MoveCallback moveCallback, bool bMultiSelect,
    ActionFinishedMode finishedMode, bool isReturnKeyNeedCanceled)
    :m_pned(exchangeData)
    ,m_moveCallback(moveCallback)
    ,m_bMultiSelectMode(bMultiSelect)
    ,m_isPickNullReturnPos(false)
    ,m_pickedPos(nullptr)
    ,m_finishedMode(finishedMode)
    ,m_OnKeyDownCallback(nullptr)
{
    m_pned.pGNodeReferencesSelection->clear();
    //m_oPickSketchObjectsFirstEventHandler = NEW_AS_OWNER_PTR(PickSketchObjectsFirstEventHandler);
    //GetPickPostProcessEvent()->Add(m_oPickSketchObjectsFirstEventHandler.get());
    SetReturnKeyNeedCanceled(isReturnKeyNeedCanceled);
    m_oActionUiBehavior = NEW_AS_OWNER_PTR(ActionUiBehavior, m_bMultiSelectMode);
}

SamplePickNodeReferenceAction::~SamplePickNodeReferenceAction()
{
    CleanupTempGraphicsShape();
    if (m_oActionUiBehavior)
        m_oActionUiBehavior->UnInitializeUi();

    IPickNodeReferenceAction::SetCursorPathCallBack(nullptr);
}

void SamplePickNodeReferenceAction::AddPostProcesserEventHandler(gcmp::IPickEventHandler* pPostEventHandler)
{
    GetPickPostProcessEvent()->Add(pPostEventHandler);
}

void SamplePickNodeReferenceAction::SetPickFilter(gcmp::OwnerPtr<IPickFilter> upPickFilter)
{
    m_upPickFilter = TransferOwnership(upPickFilter);
    if (m_upPickFilter)
    {
        m_upPickFilter->SetPickTargetOption(m_pickTarget.get());
    }
}

void SamplePickNodeReferenceAction::GetCurrentMousePosition(IUiView* pCurrentView,const Vector3d& pos)
{
    SAFE_INIT_POINTER(m_pned.pMousePoint ,pos);
    if(m_pned.bMouseOnWorkPlane)
    {
        Vector3d posOnWP;
        if(ProjectPositionOnWorkPlane(pCurrentView, pos, posOnWP))
        {
            SAFE_INIT_POINTER(m_pned.pMousePoint, posOnWP);
        }
    }

}

void SamplePickNodeReferenceAction::InitAction(gcmp::IUiView* pCurrentView)
{
    SamplePickActionBase::InitAction(pCurrentView);

    if (m_oActionUiBehavior)
        m_oActionUiBehavior->InitializeUi();
}

bool SamplePickNodeReferenceAction::OnLButtonDown(IUiView* pCurrentView, const Vector3d& pos)
{
    GetCurrentMousePosition(pCurrentView,pos);
    if (!SamplePickActionBase::OnLButtonDown(pCurrentView,pos) )
    {
        //m_bPickElementShapeHandle为true且m_auxElementId有效时，视为拾取到了辅助对象
        if (m_bPickElementShapeHandle && m_auxElementId.IsValid())
        {
            DBG_WARN_AND_RETURN_FALSE_UNLESS(m_pned.pPickedAuxiliaryElementId, L"若需拾取辅助对象，请传入一个ElementId的指针用于记录辅助对象的Id",L"GDMP",L"2024-03-30");
            *(m_pned.pPickedAuxiliaryElementId) = m_auxElementId;
            MarkFinishStatus(ActionFinishStatus::Successful);
            return true;
        }
        //m_isPickNullReturnPos为true且没有拾取到东西时，结束命令并返回拾取点在草图平面的坐标
        if (m_isPickNullReturnPos)
        {
            Vector3d posOnWP;
            if (ActionBase::ProjectPositionOnWorkPlane(pCurrentView, pos, posOnWP))
            {
                *m_pickedPos = posOnWP;
            }
            MarkFinishStatus(ActionFinishStatus::Successful);
            return true;
        }
        //否则继续拾取
        return false;
    }

    if (!m_bMultiSelectMode)//单选
    {
        SAFE_INIT_POINTER(m_pned.pUserCancelled, false);
        MarkFinishStatus(ActionFinishStatus::Successful);
    }

    return true;
}

bool SamplePickNodeReferenceAction::OnLButtonUp(IUiView* pCurrentView, const Vector3d& pos)
{
    GetCurrentMousePosition(pCurrentView, pos);
    if (!m_pned.bSingleSelectMode)
    {
        SamplePickActionBase::OnLButtonUp(pCurrentView, pos);
    }
    m_status = (unsigned int)GcmpEnPickStatus::PS_LBUTTON_UP;
    
    if (!m_pned.bSingleSelectMode && !m_bMultiSelectMode && m_pned.pGNodeReferencesSelection->size() > 0)//单选
    {
        MarkFinishStatus(ActionFinishStatus::Successful);
    }

    return true;
}

bool SamplePickNodeReferenceAction::OnRButtonDown(IUiView* pCurrentView, const Vector3d& pos)
{
    if (!m_bMultiSelectMode)
    {  
        if (m_finishedMode == ActionFinishedMode::RButtonDown)
        {
            SAFE_INIT_POINTER(m_pned.pUserCancelled, true);
            MarkFinishStatus(ActionFinishStatus::Cancelled);
            MarkFinishReason(ActionFinishReason::RButtonDown);
        }
    }
    return true;
}

bool SamplePickNodeReferenceAction::OnRButtonUp(IUiView* pCurrentView, const Vector3d& pos)
{
    if (!m_bMultiSelectMode)
    {
        GetCurrentMousePosition(pCurrentView, pos);
        SamplePickActionBase::OnLButtonUp(pCurrentView, pos);
        if (m_finishedMode == ActionFinishedMode::RButtonUp)
        {
            SAFE_INIT_POINTER(m_pned.pUserCancelled, true);
            MarkFinishStatus(ActionFinishStatus::Cancelled);
            MarkFinishReason(ActionFinishReason::RButtonUp);
        }
    }
    return true;
}

OwnerPtr<gcmp::IMenuItemContainerDefinition> SamplePickNodeReferenceAction::PrepareContextMenu(IUiView* pUIView)
{
    if (!m_bMultiSelectMode && (m_finishedMode == ActionFinishedMode::RButtonDown ||
        m_finishedMode == ActionFinishedMode::RButtonUp))
    {
        return nullptr;
    }
    const std::wstring defActionMenuId = L"DefActionMenu";
    OwnerPtr<IMenuItemContainerDefinition> pMenuItemContainer = IMenuItemContainerDefinition::Create(defActionMenuId, L"");
    {
        if ((m_pned.pGNodeReferencesSelection)->size() > 0 || (m_bPickElementShapeHandle && m_pned.pPickedAuxiliaryElementId && m_pned.pPickedAuxiliaryElementId->IsValid()))
        {
            OwnerPtr<IMenuItemDefinition> pDefMenuItem = ICommandMenuItemDefinition::Create(ID_CMD_ACTION_CONFIRM, ID_CMD_ACTION_CONFIRM, GBMP_TR(L"确定"), GBMP_TR(L"确定"), L"");
            pMenuItemContainer->AddSubMenuItem(TransferOwnership(pDefMenuItem));
        }
    }
    {
        OwnerPtr<IMenuItemDefinition> pDefMenuItem = ICommandMenuItemDefinition::Create(ID_CMD_ACTION_ESCAPE, ID_CMD_ACTION_ESCAPE, GBMP_TR(L"取消"), GBMP_TR(L"取消"), L"");
        pMenuItemContainer->AddSubMenuItem(TransferOwnership(pDefMenuItem));
    }

    return TransferOwnership(pMenuItemContainer);
}

bool SamplePickNodeReferenceAction::OnMovePoint(IUiView* pCurrentView, const Vector3d& pos)
{
    GetCurrentMousePosition(pCurrentView,pos);
    if (m_status == (unsigned int)GcmpEnPickStatus::PS_LBUTTON_DOWN && (m_pned.bSingleSelectMode))
    {
        return true;
    }
    //单选选项
    SamplePickActionBase::OnMovePoint(pCurrentView, pos);

    if(m_moveCallback)
    {
        m_moveCallback(pCurrentView, pos);
    }
    return true;
}

bool SamplePickNodeReferenceAction::OnKeyDown(IUiView* pCurrentView, int nChar)
{
    if (m_OnKeyDownCallback)
    {
        //返回值为ture，退出OnKeyDown； 返回值为false，继续执行OnKeyDown的默认行为
        Vector3d pos = m_pned.pMousePoint ? *m_pned.pMousePoint : Vector3d::Zero;
        if (m_OnKeyDownCallback(nChar, pCurrentView, pos))
        {
            return true;
        }
    }

    if (nChar == VK_DELETE && IsInMultiSelectMode())//多选模式下，禁用delete
        return true;

    SamplePickActionBase::OnKeyDown(pCurrentView, nChar);
    
    // 如果有执行delete操作，则不进入预览编辑状态
    // 场景：通过拾取创建参照平面在拾取状态时，偏移的预览时等
    if (nChar != VK_DELETE && m_moveCallback && m_pned.pMousePoint)
    {
        Vector3d pos = *m_pned.pMousePoint;
        m_moveCallback(pCurrentView, pos);
    }
    return true;
}

void SamplePickNodeReferenceAction::AddToSelection(IDocument* pDocument, const IGraphicsNodeReference& pickResult)
{
    IDocument* pDoc = pDocument;

    if (m_bMultiSelectMode)//多选模式
    {
        if (IsKeyAndButtonPressed(VK_CONTROL))
        {
            AddNodeReference(&pickResult);
        }
        else if (IsKeyAndButtonPressed(VK_SHIFT))
        {
            auto iter = FindInContainer(*m_pned.pGNodeReferencesSelection, &pickResult);

            //前提是vector中没有相同的元素
            if (iter != m_pned.pGNodeReferencesSelection->end())
            {
                m_pned.pGNodeReferencesSelection->erase(iter);
            }
        }
        else
        {
            m_pned.pGNodeReferencesSelection->clear();
            AddNodeReference(&pickResult);
        }
        IHighlights::Get()->SetGraphicsNodeReferences(*m_pned.pGNodeReferencesSelection);
    }
    else//单选模式
    {
        m_pned.pGNodeReferencesSelection->clear();
        AddNodeReference(&pickResult);
        IHighlights::Get()->SetGraphicsNodeReferences(*m_pned.pGNodeReferencesSelection);
    }

    return;
}

void SamplePickNodeReferenceAction::AddNodeReferenceGroup(IDocument* pDoc,const GraphicsNodeReferenceOwnerPtrSet& pickResults)
{
    auto iter = pickResults.begin();
    for (;iter!=pickResults.end();++iter)
    {
        if(!OwnerPtrContainerUtil::IsInContainer(*m_pned.pGNodeReferencesSelection, *iter))
            OwnerPtrContainerUtil::AddItem(*m_pned.pGNodeReferencesSelection, **iter);
    }
}
void SamplePickNodeReferenceAction::DeleteNodeReference(IDocument* pDoc,const GraphicsNodeReferenceOwnerPtrSet& pickResults)
{
    auto removeHelper = [&](const OwnerPtr<IGraphicsNodeReference>& opGnodeRef)->bool {
        if (pickResults.find(opGnodeRef) != pickResults.end())
            return true;
        return false;
    };

    auto& selection = *m_pned.pGNodeReferencesSelection;
    selection.erase(std::remove_if(selection.begin(), selection.end(), removeHelper), selection.end());
}

void SamplePickNodeReferenceAction::AddToSelectionGroup(IDocument* pDoc, GraphicsNodeReferenceOwnerPtrSet& pickResults)
{
    if (m_bMultiSelectMode)//多选
    {
        bool ctrlKeyPressed = IsKeyAndButtonPressed(VK_CONTROL);
        bool shiftKeyPressed = IsKeyAndButtonPressed(VK_SHIFT);

        //1. Ctrl 总是加
        if (ctrlKeyPressed && !shiftKeyPressed)
        {
            FOR_EACH(gnode, pickResults)
            {
                AddNodeReference(gnode.get());
            }
        }

        //2. Shift总是减
        if (!ctrlKeyPressed && shiftKeyPressed)
        {
            FOR_EACH(gnode, pickResults)
            {
                OwnerPtrContainerUtil::RemoveItem(*m_pned.pGNodeReferencesSelection, *gnode);
            }
        }

        //3. 不按ctrl和shift总是替换
        if (!ctrlKeyPressed && !shiftKeyPressed)
        {
            m_pned.pGNodeReferencesSelection->clear();
            FOR_EACH(gnode, pickResults)
            {
                AddNodeReference(gnode.get());
            }
        }
        IHighlights::Get()->SetGraphicsNodeReferences(*m_pned.pGNodeReferencesSelection);
    }
    else//单选
    {
        m_pned.pGNodeReferencesSelection->clear();
        FOR_EACH(gnode, pickResults)
        {
            AddNodeReference(gnode.get());
        }
        IHighlights::Get()->SetGraphicsNodeReferences(*m_pned.pGNodeReferencesSelection);
    }
}

void SamplePickNodeReferenceAction::AddNodeReference( const IGraphicsNodeReference* pickResult )
{
    for (auto it=m_pned.pGNodeReferencesSelection->begin(); it!=m_pned.pGNodeReferencesSelection->end(); ++it)
    {
        if ((*it)->IsSameGraphicsNode(*pickResult)) return;
    }

    OwnerPtrContainerUtil::AddItem(*m_pned.pGNodeReferencesSelection, pickResult);
}

void SamplePickNodeReferenceAction::ActionCancelled()
{
    //通知基类，会标记当前Action结束
    ActionBase::ActionCancelled();

    //多选的状态下，清空之前选择的内容
    if (m_pned.pGNodeReferencesSelection != nullptr)
    {
        m_pned.pGNodeReferencesSelection->clear();
    }

    SAFE_INIT_POINTER(m_pned.pUserCancelled, true);
}

void SamplePickNodeReferenceAction::ClearSelection( IDocument* pDoc )
{
    //SamplePickActionBase::ClearSelection(pDoc);
    IHighlights::Get()->Clear();
    if (m_pned.pGNodeReferencesSelection)
    {
        m_pned.pGNodeReferencesSelection->clear();
    }
}

bool SamplePickNodeReferenceAction::IsSelectionEmpty()
{
    if (m_pned.pGNodeReferencesSelection)
    {
        return m_pned.pGNodeReferencesSelection->empty();
    }
    //单选没有空集合概念
    return false;
}

void SamplePickNodeReferenceAction::OnSelectionChanged()
{
    //zhangzg: 这个实现没有任何意义，直接删除，这样可以拆除SamplePickNodeReferenceAction对QT控件的依赖，能够直接编译为JS

    //if(PropertyPalette* pPP = PropertyPalette::GetPropertyPalette())
    //{
    //    pPP->Refresh();
    //}
}

std::wstring SamplePickNodeReferenceAction::GetCursorPath() const
{
    IPickNodeReferenceAction::PickNodeReferenceActionCursorPathCallBack cursorPathCallBack = IPickNodeReferenceAction::GetCursorPathCallBack();
    
    std::wstring cursorPath;
    if (cursorPathCallBack)
        cursorPathCallBack(cursorPath);

    if (cursorPath.empty())
        cursorPath = ActionBase::GetCursorPath();

    return cursorPath;
}

void SamplePickNodeReferenceAction::SetPickNullReturnPos(bool isPickNullReturnPos, Vector3d* pickedPos)
{
    m_isPickNullReturnPos = isPickNullReturnPos;
    m_pickedPos = pickedPos;
}

void SamplePickNodeReferenceAction::OnDlgButtonClicked(bool isOKClicked)
{
    if (!isOKClicked)
    {
        MarkFinishStatus(ActionFinishStatus::Cancelled);
        IUiDocumentViewManager* pUiDocViewMgr = IUiDocumentViewManager::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pUiDocViewMgr, L"pUiDocViewMgr为空",L"GDMP",L"2024-03-30");
        gcmp::IUiDocument* pUIDoc = pUiDocViewMgr->GetCurrentUiDocument();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pUIDoc, L"pUIDoc为空",L"GDMP",L"2024-03-30");
        gcmp::IDocument* pDoc = pUIDoc->GetDbDocument();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pUIDoc, L"pDoc为空",L"GDMP",L"2024-03-30");

        if(m_pned.pGNodeReferencesSelection)
            m_pned.pGNodeReferencesSelection->clear();
        IHighlights::Get()->Clear();
    }
    else
    {
        MarkFinishStatus(ActionFinishStatus::Successful);
    }
    
    KillMe();
}

bool SamplePickNodeReferenceAction::SetOnKeyDownCallback(IPickNodeReferenceAction::OnKeyDownCallback callback)
{
    m_OnKeyDownCallback = callback;
    return true;
}

void SamplePickNodeReferenceAction::SetActionUiBehavior(OwnerPtr<IActionUiBehavior> opActionUiBehavior)
{
    m_oActionUiBehavior = TransferOwnership(opActionUiBehavior);
}

gcmp::OwnerPtr<gcmp::IAction> Sample::SamplePickNodeReferenceAction::Create(gcmp::PickNodeReferenceExchangeData & exchangeData, 
    gcmp::OwnerPtr<gcmp::IPickFilter> opPickFilter, const gcmp::PickNodeReferenceOption & options, bool isReturnKeyNeedCanceled)
{
    OwnerPtr<SamplePickNodeReferenceAction> oPickNodeRefAction = NEW_AS_OWNER_PTR(SamplePickNodeReferenceAction, exchangeData,
        options.moveCallback, options.bMultiSelect, options.FinishedMode, isReturnKeyNeedCanceled);
    if (oPickNodeRefAction)
    {
        oPickNodeRefAction->SetPickFilter(TransferOwnership(opPickFilter));
        oPickNodeRefAction->SetCursorType(options.cursorType);

        if (options.pPostEventHandler != nullptr)
        {
            oPickNodeRefAction->AddPostProcesserEventHandler(options.pPostEventHandler);
        }

        oPickNodeRefAction->SetPickNullReturnPos(options.isPickNullReturnPos, options.pickedPos);
        oPickNodeRefAction->SetIsAllowedMove(options.isMoveAllowed);
        oPickNodeRefAction->SetIsPickingHighlightOnlyGraphicsNodeAllowed(options.isPickingHighlightOnlyGraphicsNodeAllowed);

        oPickNodeRefAction->SetMouseTooltipMessage(options.mouseTooltipMessage);

        oPickNodeRefAction->SetPickPixelTolerance(options.pickPixelTolerance);
        oPickNodeRefAction->SetPickAuxiliaryElement(options.isPickElementShapeHandle);

        if (options.opRequest.get() != nullptr)
            oPickNodeRefAction->PostRequest(TransferOwnership((const_cast<PickNodeReferenceOption&>(options)).opRequest));

        if (options.keyDownCallback != nullptr)
        {
            oPickNodeRefAction->SetOnKeyDownCallback(options.keyDownCallback);
        }

        if (options.opUiBehavior.get() != nullptr)
        {
            oPickNodeRefAction->SetActionUiBehavior(TransferOwnership(options.opUiBehavior->Clone()));
        }
    }

    return TransferOwnership(oPickNodeRefAction);
}
